﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

#pragma warning disable 0659

/*
 * Zadatak: ispitati da li se sve konverzije ponasaju isto kao u Matlab-u
 */

namespace AtomicTypes
{
    public interface IAtomicType
    {
    }
    public abstract class AtomicTypeValue<T>
    {
        protected T _real;
        protected T _imag;
        public T Real { get { return _real; } } 
        public T Imag { get { return _imag; } }
        protected AtomicTypeValue(T real, T imag)
        {
            _real = real;
            _imag = imag;
        }
        abstract public bool IsComplex();

        public override string ToString()
        {
            if (!this.IsComplex())
                return _real.ToString();
            else
                return _real.ToString() + " + " + _imag.ToString() + "i";
        }
    }

    /*
     * logical class
     */
    public class QBoolean : AtomicTypeValue<bool>, IAtomicType
    {
        public QBoolean(bool val)
            : base(val, false) { }

        public QBoolean(QInt8 val) 
            : base(val.Real!=(short)0, false) 
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to logical");
        }

        public QBoolean(QInt16 val)
            : base(val.Real != (short)0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int16> to logical");
        }
        public QBoolean(QInt32 val)
            : base(val.Real != 0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int32> to logical");
        }

        public QBoolean(QInt64 val)
            : base(val.Real != (long)0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int64> to logical");
        }

        public QBoolean(QUInt8 val)
            : base(val.Real != (ushort)0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<uint8> to logical");
        }

        public QBoolean(QUInt16 val)
            : base(val.Real != (ushort)0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<uint16> to logical");
        }
        public QBoolean(QUInt32 val)
            : base(val.Real != 0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<uint32> to logical");
        }

        public QBoolean(QUInt64 val)
            : base(val.Real != (ulong)0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<uint64> to logical");
        }

        public QBoolean(QBoolean val)
            : base(val._real, false) { }

        public QBoolean(QFloat32 val)
            : base(val.Real != (float)0.0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<single> to logical");
        }

        public QBoolean(QFloat64 val)
            : base(val.Real != 0.0, false)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<double> to logical");
        }

        public override bool IsComplex() { return false; }
    }
    
    /*
     * numeric classes
     */
    // floating-point classes
    public class QFloat32 : AtomicTypeValue<float>, IAtomicType  // aka SINGLE
    {
        public QFloat32(float real, float imag = (float) 0.0)
            : base(real, imag) { }

        public QFloat32(QBoolean val)
            : base(val.Real ? (float)1 : (float)0, (float) 0.0) { }

        public QFloat32(QInt8 val)
            : base((float)val.Real, (float)val.Imag) { }

        public QFloat32(QInt16 val)
            : base((float)val.Real, (float)val.Imag) { }
        
        public QFloat32(QInt32 val)
            : base((float)val.Real, (float)val.Imag) { }
        
        public QFloat32(QInt64 val)
            : base((float)val.Real, (float)val.Imag) { }

        public QFloat32(QUInt8 val)
            : base((float)val.Real, (float)val.Imag) { }

        public QFloat32(QUInt16 val)
            : base((float)val.Real, (float)val.Imag) { }

        public QFloat32(QUInt32 val)
            : base((float)val.Real, (float)val.Imag) { }

        public QFloat32(QUInt64 val)
            : base((float)val.Real, (float)val.Imag) { }

        public QFloat32(QFloat32 val)
            : base(val.Real, val.Imag) { }

        public QFloat32(QFloat64 val)
            : base((float)val.Real, (float)val.Imag) { }

        public QFloat32(QChar val)
            : base((float)val.Real, (float) 0.0) { } // ???

        public override bool IsComplex() { return _imag != (float) 0.0; }
    }

    public class QFloat64 : AtomicTypeValue<double>, IAtomicType // aka DOUBLE
    {
        public QFloat64(double real, double imag = 0.0)
            : base(real, imag) { }

        public QFloat64(QBoolean val)
            : base(val.Real ? (double)1 : (double)0, (double) 0.0) { }

        public QFloat64(QInt8 val)
            : base((double)val.Real, (double)val.Imag) { }

        public QFloat64(QInt16 val)
            : base((double)val.Real, (double)val.Imag) { }
        
        public QFloat64(QInt32 val)
            : base((double)val.Real, (double)val.Imag) { }
        
        public QFloat64(QInt64 val)
            : base((double)val.Real, (double)val.Imag) { }

        public QFloat64(QUInt8 val)
            : base((double)val.Real, (double)val.Imag) { }

        public QFloat64(QUInt16 val)
            : base((double)val.Real, (double)val.Imag) { }

        public QFloat64(QUInt32 val)
            : base((double)val.Real, (double)val.Imag) { }

        public QFloat64(QUInt64 val)
            : base((double)val.Real, (double)val.Imag) { }

        public QFloat64(QFloat32 val)
            : base(val.Real, val.Imag) { }

        public QFloat64(QFloat64 val)
            : base((double)val.Real, (double)val.Imag) { }

        public QFloat64(QChar val)
            : base((double)val.Real, (double) 0.0) { } // ??? 

      

        public override bool IsComplex() { return _imag != 0.0; }
    }
    
    /*
     * signed integer classes
     */
    public class QInt8 : AtomicTypeValue<short>, IAtomicType 
    {
        public QInt8(short real, short imag = 0)
            : base(real, imag) 
        { fixValues(); }

        public QInt8(QInt8 val)
            : base(val.Real, val.Imag) { }

        public QInt8(QInt16 val)
            : base(val.Real, val.Imag)
        { fixValues();  }

        public QInt8(QInt32 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }
        
        public QInt8(QInt64 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }

        public QInt8(QUInt8 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }

        public QInt8(QUInt16 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }

        public QInt8(QUInt32 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }

        public QInt8(QUInt64 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }

        public QInt8(QBoolean val)
            : base(val.Real ? (short)1 : (short)0, (short)0) { }

        public QInt8(QFloat32 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }

        public QInt8(QFloat64 val)
            : base((short)val.Real, (short)val.Imag)
        { fixValues(); }

        public QInt8(QChar val)
            : base((short)val.Real, (short)0)
        { fixValues(); }

        public override bool IsComplex() { return _imag != (short)0; }

        private void fixValues()
        {
            _real = _real >  127 ? (short) 127 : _real;
            _real = _real < -128 ? (short)-128 : _real;
            _imag = _imag >  127 ? (short) 127 : _imag;
            _imag = _imag < -128 ? (short)-128 : _imag;
        }
    }

    public class QInt16 : AtomicTypeValue<short>, IAtomicType 
    { 
        // PAZNJA: 32767 + 1 -> 32767, u Matlabu
        public QInt16(short real, short imag = 0)
            : base(real, imag) { }

        public QInt16(QInt8 val)
            : base(val.Real, val.Imag) { }

        public QInt16(QInt16 val)
            : base(val.Real, val.Imag) { }

        public QInt16(QInt32 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QInt64 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QUInt8 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QUInt16 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QUInt32 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QUInt64 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QBoolean val)
            : base(val.Real ? (short)1 : (short)0, (short)0) { }

        public QInt16(QFloat32 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QFloat64 val)
            : base((short)val.Real, (short)val.Imag) { }

        public QInt16(QChar val)
            : base((short)val.Real, (short)0) { }

        public override bool IsComplex() { return _imag != (short)0; }
    }

    public class QInt32 : AtomicTypeValue<int>, IAtomicType 
    { 
        public QInt32(int real, int imag = 0)
            : base(real, imag) { }

        public QInt32(QInt8 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QInt16 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QInt32 val)
            : base(val.Real, val.Imag) { }

        public QInt32(QInt64 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QUInt8 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QUInt16 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QUInt32 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QUInt64 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QBoolean val)
            : base(val.Real ? (int)1 : (int)0, (int)0) { }

        public QInt32(QFloat32 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QFloat64 val)
            : base((int)val.Real, (int)val.Imag) { }

        public QInt32(QChar val)
            : base((int)val.Real, (int)0) { }

        public override bool IsComplex() { return _imag != 0; }
    }

    public class QInt64 : AtomicTypeValue<long>, IAtomicType 
    { 
        public QInt64(long real, long imag = 0)
            : base(real, imag) { }

        public QInt64(QInt8 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QInt16 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QInt32 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QInt64 val)
            : base(val.Real, val.Imag) { }

        public QInt64(QUInt8 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QUInt16 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QUInt32 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QUInt64 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QBoolean val)
            : base(val.Real ? (long)1 : (long)0, (long)0) { }

        public QInt64(QFloat32 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QFloat64 val)
            : base((long)val.Real, (long)val.Imag) { }

        public QInt64(QChar val)
            : base((long)val.Real, (long)0) { }

        public override bool IsComplex() { return _imag != (long)0; }
    }
    
    /*
     * unsigned integer classes
     */
    public class QUInt8 : AtomicTypeValue<ushort>, IAtomicType 
    {
        public QUInt8(ushort real, ushort imag = 0)
            : base(real, imag) 
        {
            fixValues();
        }

        public QUInt8(QInt8 val)
            : base((ushort)val.Real, (ushort)val.Imag) 
        { fixValues(); }

        public QUInt8(QInt16 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues();  }

        public QUInt8(QInt32 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }
        
        public QUInt8(QInt64 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }

        public QUInt8(QUInt8 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }

        public QUInt8(QUInt16 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }

        public QUInt8(QUInt32 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }

        public QUInt8(QUInt64 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }

        public QUInt8(QBoolean val)
            : base(val.Real ? (ushort)1 : (ushort)0, (ushort)0) { }

        public QUInt8(QFloat32 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }

        public QUInt8(QFloat64 val)
            : base((ushort)val.Real, (ushort)val.Imag)
        { fixValues(); }

        public QUInt8(QChar val)
            : base((ushort)val.Real, (ushort)0)
        { fixValues(); }

        public override bool IsComplex() { return _imag != (ushort)0; }

        public void fixValues()
        {
            _real = _real > 255 ? (ushort)255 : _real;
            _real = _real <   0 ? (ushort)  0 : _real;
            _imag = _imag > 255 ? (ushort)255 : _imag;
            _imag = _imag <   0 ? (ushort)  0 : _imag;
        }
    }

    public class QUInt16 : AtomicTypeValue<ushort>, IAtomicType 
    { 
        public QUInt16(ushort real, ushort imag = 0)
            : base(real, imag) { }

        public QUInt16(QInt8 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QInt16 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QInt32 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QInt64 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QUInt8 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QUInt16 val)
            : base(val.Real, val.Imag) { }

        public QUInt16(QUInt32 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QUInt64 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QBoolean val)
            : base(val.Real ? (ushort)1 : (ushort)0, (ushort)0) { }

        public QUInt16(QFloat32 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QFloat64 val)
            : base((ushort)val.Real, (ushort)val.Imag) { }

        public QUInt16(QChar val)
            : base((ushort)val.Real, (ushort)0) { }

        public override bool IsComplex() { return _imag != (ushort)0; }
    }

    public class QUInt32 : AtomicTypeValue<UInt32>, IAtomicType 
    { 
        public QUInt32(uint real, uint imag = 0)
            : base(real, imag) { }

        public QUInt32(QInt8 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QInt16 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QInt32 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QInt64 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QUInt8 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QUInt16 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QUInt32 val)
            : base(val.Real, val.Imag) { }

        public QUInt32(QUInt64 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QBoolean val)
            : base(val.Real ? (uint)1 : (uint)0, (uint)0) { }

        public QUInt32(QFloat32 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QFloat64 val)
            : base((uint)val.Real, (uint)val.Imag) { }

        public QUInt32(QChar val)
            : base((uint)val.Real, (uint)0) { }

        public override bool IsComplex() { return _imag != (uint) 0; }
    }

    public class QUInt64 : AtomicTypeValue<ulong>, IAtomicType 
    { 
        public QUInt64(ulong real, ulong imag = 0)
            : base(real, imag) { }

        public QUInt64(QInt8 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QInt16 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QInt32 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QInt64 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QUInt8 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QUInt16 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QUInt32 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QUInt64 val)
            : base(val.Real, val.Imag) { }

        public QUInt64(QBoolean val)
            : base(val.Real ? (ulong)1 : (ulong)0, (ulong)0) { }

        public QUInt64(QFloat32 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QFloat64 val)
            : base((ulong)val.Real, (ulong)val.Imag) { }

        public QUInt64(QChar val)
            : base((ulong)val.Real, (ulong)0) { }


        public override bool IsComplex() { return _imag != (ulong) 0; }
    }
    
    
    /*
     * character class
     */
    public class QChar : AtomicTypeValue<char>, IAtomicType 
    { 
        public QChar(char val)
            : base(val, (char)0) { }

        public QChar(QInt8 val)
            : base((char)val.Real, (char)0) 
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QInt16 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QInt32 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QInt64 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QUInt8 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QUInt16 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QUInt32 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QUInt64 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QChar val)
            : base(val.Real, (char)0) { }

        public QChar(QFloat32 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public QChar(QFloat64 val)
            : base((char)val.Real, (char)0)
        {
            if (val.IsComplex())
                throw new Exception("Can't convert from complex<int8> to char");
        }

        public override bool IsComplex() { return false; }
    }

}
